﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "CmdAutoRecognizeStructureFloor.h"
#include "GmUiStructureCommandRegister.h"
#include "StructureCmdIds.h"
#include "CmdStatusUtils.h"

#include "ISelection.h"
#include "IInstance.h"
#include "IMainWindow.h"
#include "IBody.h"
#include "IGraphicsNodeReference.h"
#include "IElementModelShape.h"
#include "IGraphicsBRepBody.h"
#include "IGraphicsElementShape.h"
#include "IDocument.h"
#include "IModelView.h"
#include "IFamily.h"
#include "IInstanceType.h"
#include "IPolygon.h"
#include "IUserTransaction.h"
#include "IElementBasicInformation.h"
#include "UiCommonDialog.h"
#include "Coordinate3d.h"

#include "ILevel.h"
#include "LevelUtils.h"
#include "IStructureInstanceBuildingStoreyData.h"
#include "GbmpAutoRecognizeStructureFloorParameterDlg.h"
#include "GbmpModelViewTypes.h"
#include "GbmpModelViewUtil.h"
#include "GbmpBeamUtils.h"
#include "GbmpWallUtils.h"
#include "GbmpColumnUtils.h"
#include "GbmpGraphicsNodeUtils.h"
#include "IStructureFloor.h"
#include "StructureFloorUtils.h"
#include "StructureProfileInstanceInput.h"
#include "StructureProfileInstanceUtils.h"
#include "InteractivePicker.h"
#include "AutoRecognizeStructureFloorPickFilter.h"
#include "UiDocumentViewUtils.h"

#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

using namespace gcmp;

namespace
{
    OwnerPtr<IBody> GrepToGbmpBody(const IDocument* pDoc, const IGraphicsElementShape* pGrep)
    {
        OwnerPtr<IGraphicsBRepBody> opBrep = GbmpGraphicsNodeUtils::GetGRepBody(pDoc, pGrep);
        const IBody* pBody = opBrep->GetGeometry();
        return TransferOwnershipCast<IBody>(pBody->Clone());
    }

    void GetWorkLevelBodies(const IDocument* pDoc, const std::vector<ElementId>& selectedElements, ElementId workLevelId, std::vector<OwnerPtr<IBody>>& opAllBodies)
    {
        FOR_EACH(elementId, selectedElements)
        {
            IInstance* pInstance = quick_cast<IInstance>(pDoc->GetElement(elementId));
            DBG_WARN_AND_RETURN_VOID_UNLESS(pInstance, L"pInstance为空",L"GDMPLab",L"2024-03-30");
            const IStructureInstanceBuildingStoreyData* pBuildingStoreyData = IStructureInstanceBuildingStoreyData::Get(pInstance);
            if(pBuildingStoreyData->GetBuildingStoreyId() != workLevelId)
            {
                continue;
            }

            if (GbmpBeamUtils::IsBeam(pInstance) || GbmpWallUtils::IsWall(pInstance) || GbmpColumnUtils::IsColumn(pInstance))
            {
                const IGraphicsElementShape* pGrep = pInstance->GetElementModelShape()->GetGraphicsElementShape();
                OwnerPtr<IBody> opBody = GrepToGbmpBody(pDoc, pGrep);
                DBG_WARN_AND_RETURN_VOID_UNLESS(opBody.get(), L"opBody为空",L"GDMPLab",L"2024-03-30");
                opAllBodies.push_back(TransferOwnership(opBody));
            }
        }
    }
}


REGISTER_GM_COMMAND(CmdAutoRecognizeStructureFloor)

CmdAutoRecognizeStructureFloor::CmdAutoRecognizeStructureFloor()
    : GbmpCommandBase(ID_CMD_AUTO_RECGNIZE_STRUCTURE_FLOOR)
{
}

CmdAutoRecognizeStructureFloor::~CmdAutoRecognizeStructureFloor()
{
}

bool CmdAutoRecognizeStructureFloor::IsEnabled() const
{
    if (!CmdStatusUtils::IsInProjectEnvironment())
    {
        return false;
    }

    IModelView* pModelView = UiDocumentViewUtils::GetCurrentModelView();
    if (!pModelView)
        return false;

    const std::wstring& viewType = pModelView->GetUserDataId();

    if (PlanViewType == viewType)
    {
        return true;
    }

    return false;
}

bool CmdAutoRecognizeStructureFloor::IsVisible() const
{
    CMD_ONLY_VISIBLE_IN_PROJECT_ENVIRONMENT
}

gcmp::OwnerPtr<IAction> CmdAutoRecognizeStructureFloor::ExecuteCommand(const gcmp::CommandParameters& cmdParams)
{
    IDocument* pDoc = UiDocumentViewUtils::GetCurrentDocument();
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pDoc, L"pDoc为空",L"GDMPLab",L"2024-03-30");

    IModelView* pModelView = UiDocumentViewUtils::GetCurrentModelView();
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pModelView, L"pModelView为空",L"GDMPLab",L"2024-03-30");

    // 当前视图工作楼层
    ElementId workLevelId = GbmpModelViewUtil::GetWorkLevelId(pModelView);
    DBG_WARN_AND_RETURN_FALSE_UNLESS(workLevelId.IsValid(), L"workLevelId无效",L"GDMPLab",L"2024-03-30");

    GbmpAutoRecognizeStructureFloorParameterDlg dlg(pDoc, workLevelId, dynamic_cast<QWidget*>(IMainWindow::GetMainWindow()));
    if (!dlg.exec())
    {
        return nullptr;
    }

    const StructureFloorCreateParameter floorCreateParam = dlg.GetStructureFloorCreateParameter();
    const std::wstring& floorFamilyName = floorCreateParam.floorFamilyName;
    const std::wstring& floorType = floorCreateParam.floorType;
    ElementId buildingStoreyLevelId = floorCreateParam.buildingStoreyLevelId;
    ElementId topLevelId = floorCreateParam.topLevelId;
    double topLevelOffset = floorCreateParam.topLevelOffset;
    bool isFromSelected = floorCreateParam.isFromSelected;

    std::vector<ElementId> selectedElements;
    if (isFromSelected && ISelection::Get()->IsEmpty())  // 如果选择集为空，则调用按范围选择功能
    {
        ILevel* pLevel = LevelUtils::GetLevelById(pDoc, workLevelId);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pLevel, L"pLevel为空",L"GDMPLab",L"2024-03-30");

        std::wstring levelName = pLevel->GetOwnerElement()->GetBasicInformation()->GetName();
        std::wstring mouseTooltip = L"请选择范围识别楼板（当前楼层：" + levelName + L"）";

        OwnerPtr<AutoRecognizeStructureFloorPickFilter> pPickFilter = NEW_AS_OWNER_PTR(AutoRecognizeStructureFloorPickFilter, pDoc);
        Vector3d mousePoint;
        GraphicsNodeReferenceOwnerPtrVector nodeReferenceObjects;
        InteractivePicker::Select(true, false, TransferOwnership(pPickFilter), mouseTooltip, mouseTooltip, mousePoint, nodeReferenceObjects);
        FOR_EACH(selectedItem, nodeReferenceObjects)
        {
            selectedElements.push_back(selectedItem->GetElementId());
        }
    }
    else
    {
        selectedElements = this->GetAllSelectedElements(pDoc, isFromSelected);
    }

    if (selectedElements.empty())
    {
        return nullptr;
    }

    // 自动识别创建楼板
    this->AutoRecognizeCreateStructureFloor(pDoc, selectedElements, workLevelId,
        floorFamilyName, floorType, topLevelId, topLevelOffset, buildingStoreyLevelId);

    return nullptr;
}

std::vector<ElementId> CmdAutoRecognizeStructureFloor::GetAllSelectedElements(const IDocument* pDoc, bool isFromSelected) const
{
    std::vector<ElementId> selectedElements;
    DBG_WARN_AND_RETURN_UNLESS(pDoc, selectedElements, L"pDoc为空",L"GDMPLab",L"2024-03-30");

    if (isFromSelected)
    {
        // 多选Element
        const GraphicsNodeReferenceOwnerPtrSet& nodeRefSets = ISelection::Get()->GetGraphicsNodeReferences();
        FOR_EACH(selectedItem, nodeRefSets)
        {
            ElementId selectedId = selectedItem->GetElementId();
            selectedElements.push_back(selectedId);
        }
    }
    else
    {
        selectedElements = IInstance::GetAllInstanceIds(pDoc);
    }

    return selectedElements;
}

void CmdAutoRecognizeStructureFloor::AutoRecognizeCreateStructureFloor(IDocument* pDoc, const std::vector<ElementId>& selectedElements,
    ElementId workLevelId, const std::wstring& floorFamilyName, const std::wstring& floorType, ElementId topLevelId, double topLevelOffset, ElementId buildingStoreyId)
{
    DBG_WARN_AND_RETURN_VOID_UNLESS(pDoc, L"pDoc为空",L"GDMPLab",L"2024-03-30");

    // 获取当前视图工作楼层所有墙、梁、柱
    std::vector<OwnerPtr<IBody>> opAllBodies; //所有的body
    GetWorkLevelBodies(pDoc, selectedElements, workLevelId, opAllBodies);

    // 所有构件汇总，进行识别
    if (opAllBodies.empty())
    {
        UiCommonDialog::ShowMessageBox(GBMP_TR(L"无法识别"), GBMP_TR(L"当前工作楼层没有可供识别的构件"), (int)UiCommonDialog::ButtonType::OK);
        return;
    }
    std::vector<const IBody*> pAllBodies;
    FOR_EACH(opBody, opAllBodies)
    {
        pAllBodies.push_back(opBody.get());
    }

    // 识别轮廓
    std::vector<OwnerPtr<IPolygon>> polygonList;
    StructureFloorUtils::GetFloorProfilePolygonAutoRecognizeAlgorithm(pAllBodies, Coordinate3d(Vector3d::Zero, Vector3d::UnitZ), false, polygonList);
    if (polygonList.empty())
    {
        UiCommonDialog::ShowMessageBox(GBMP_TR(L"无法识别"), GBMP_TR(L"没有检测到楼板轮廓"), (int)UiCommonDialog::ButtonType::OK);
        return;
    }

    // 根据识别结果创建楼板和板洞
    this->CreateFloorInstance(pDoc, buildingStoreyId, floorFamilyName, floorType, topLevelId, topLevelOffset, polygonList);
}

void CmdAutoRecognizeStructureFloor::CreateFloorInstance(gcmp::IDocument* pDoc, ElementId buildingStoreyId, const std::wstring& floorFamilyName, const std::wstring& floorType, ElementId topLevelId, double topLevelOffset,
    const std::vector<OwnerPtr<IPolygon>>& polygonList)
{
    OwnerPtr<IUserTransaction> opTransaction = IUserTransaction::Create(pDoc, GBMP_TR(L"自动识别创建楼板"));
    // 创建楼板和板洞
    FOR_EACH(opPolygon, polygonList)
    {
        int size = opPolygon->GetPolyCurveCount();
        DBG_WARN_AND_RETURN_VOID_UNLESS(size > 0, L"创建楼板失败",L"GDMPLab",L"2024-03-30");
        OwnerPtr<IPolyCurve> loop = opPolygon->GetPolyCurve(0);

        const IFamily* pFamily = IFamily::GetFamilyByName(pDoc, floorFamilyName);
        DBG_WARN_AND_RETURN_VOID_UNLESS(pFamily, L"pFamily为空",L"GDMPLab",L"2024-03-30");
        ElementId familyId = pFamily->GetElementId();

        const IInstanceType* pInstanceType = pFamily->GetInstanceTypeByName(floorType);
        DBG_WARN_AND_RETURN_VOID_UNLESS(pInstanceType, L"pInstanceType为空",L"GDMPLab",L"2024-03-30");
        ElementId instanceTypeId = pInstanceType->GetElementId();

        OwnerPtr<StructureProfileInstanceElementInput> opInput = NEW_AS_OWNER_PTR(StructureProfileInstanceElementInput);
        opInput->Document = pDoc;
        opInput->FamilyId = familyId;
        opInput->InstanceTypeId = instanceTypeId;
        opInput->StoreyId = buildingStoreyId;
        opInput->AssociatedPlaneId = topLevelId;
        opInput->AssociatedPlaneOffset = topLevelOffset;
        opInput->CreationOption = ElementCreationOptions::Normal;
        opInput->CanBeShared = true;
        opInput->Visible = true;

        std::vector<IModelLine*> profileCurves;
        bool bOk = StructureProfileInstanceUtils::GenerateProfileCurvesFromPolygon(pDoc, opPolygon.get(), opInput->AssociatedPlaneId, profileCurves);
        DBG_WARN_AND_RETURN_VOID_UNLESS(bOk, L"ProfileCurves生成失败",L"GDMPLab",L"2024-03-30");
        opInput->ProfileCurves = profileCurves;
        opInput->FloorSlopeLine = nullptr;
        opInput->ConcreteType = ConcreteStrengthGradeType::CSG_C30;

        IStructureFloor* pStructureFloor = IStructureFloor::Create(opInput.get());
        DBG_WARN_AND_RETURN_VOID_UNLESS(pStructureFloor, L"结构楼板创建失败",L"GDMPLab",L"2024-03-30");
    }
    opTransaction->Commit();
}
